/*
 * Copyright (C) 2018 yydcdut (yuyidong2015@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.yydcdut.sdlv;


import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.StackLayout;
import ohos.agp.components.element.Element;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.TouchEvent;

/**
 * Created by yuyidong on 2018/4/21.
 */
class DragManager implements Callback.OnDragDropListener {
    /* drag 的时候透明度 */
    private static final float DRAG_VIEW_ALPHA = 0.7f;

    /* drag 的 View */
    private Image mDragView;
    /* Inner View */
    private DragListView mDragListView;
    /* Handler的延时 */
    private final long SCROLL_HANDLER_DELAY_MILLIS = 5;
    /* 移动距离 */
    private final int DRAG_SCROLL_PX_UNIT = 25;
    /* Handler */
    private EventHandler mScrollHandler;
    /* 边界比例，到这个比例的位置就开始移动 */
    private final float BOUND_GAP_RATIO = 0.2f;
    /* 边界 */
    private int mTopScrollBound;
    private int mBottomScrollBound;
    /* 按下的时候的Y轴坐标 */
    private int mTouchDownForDragStartY;
    /* move 的时候的 Y 轴坐标 */
    private int mLastDragY;
    /* 是否进入了scroll的handler里面了 */
    private boolean mIsDragScrollerRunning = false;
    /* 最小距离 */
    private float mTouchSlop;
    /* drag 的 View 的 Bitmap */
    private Element mDragViewBitmap;
    /* drag 的误差 */
    private int mDragDelta;
    /* 是否正在被 drag */
    private boolean isDragging;
    /* 是否已经回调了 handleDragStarted */
    private boolean isInvokedDraggingStarted;
    /* 手指的 X 和 Y */
    private float mFingerX, mFingerY;
    /* decorView */
    private ComponentContainer mDecorView;
    /* 当前 View 与 decorView 的差 */
    private int[] mLeftAndTopOffset;
    /* 被 drag 的 View */
    private ItemMainLayout mItemMainLayout;
    /**
     * x、y偏离范围
     */
    private int deviationRangeX = 750, deviationRangeY = 400;

    /**
     * 是否长按了 用于区分 长按拖动 还是 触摸拖动
     */
    private boolean isLongPressClick;

    public void setLongPressClick(boolean longPressClick) {
        isLongPressClick = longPressClick;
    }


    public DragManager(Context context, DragListView dragListView, ComponentContainer decorView) {
        mDragListView = dragListView;
        mDragListView.setListDragDropListener(this);
        mDragView = new Image(context);
        mDecorView = decorView;
        mDecorView.addComponent(mDragView, new StackLayout.LayoutConfig(StackLayout.LayoutConfig.MATCH_CONTENT, StackLayout.LayoutConfig.MATCH_CONTENT));
        mLeftAndTopOffset = new int[]{0, 0};
    }

    protected void onSizeChanged() {
        mLeftAndTopOffset = new int[]{0, 0};
        getOffset(mDragListView, mDecorView, mLeftAndTopOffset);
    }

    private void getOffset(Component current, Component decorView, int[] array) {
        if (current != decorView) {
            getOffset(current, array);
            if (current.getComponentParent() != null) {
                Component view = (Component) current.getComponentParent();
                getOffset(view, decorView, array);
            }
        }
    }

    private void getOffset(Component view, int[] array) {
        array[0] = array[0] + view.getLeft() + view.getPaddingLeft();
        array[1] = array[1] + view.getTop() + view.getPaddingLeft();
    }

    protected void setDragging(boolean dragging) {
        this.isDragging = dragging;
        invokedDraggingStartedTouch();
    }

    protected boolean isDragging() {
        return isDragging;
    }

    private void invokedDraggingStarted() {
        if (isLongPressClick) {
            return;
        }
        if (!isInvokedDraggingStarted && isDragging && mFingerX != 0) {
            mDragListView.handleDragStarted((int) mFingerX, (int) mFingerY);
            isInvokedDraggingStarted = true;
            calculateBound();
        }
    }

    private void invokedDraggingStartedTouch() {
        ensureScrollHandler();
        mScrollHandler.postTask(() -> {
            if (isLongPressClick) {
                return;
            }
            mDecorView.getContext().getUITaskDispatcher().asyncDispatch(() -> {
                if (!isInvokedDraggingStarted && isDragging && mFingerX != 0) {
                    mDragListView.handleDragStarted((int) mFingerX, (int) mFingerY);
                    isInvokedDraggingStarted = true;
                    calculateBound();
                }
            });
        }, 500);

    }


    private void calculateBound() {
        final int boundGap = (int) (mDragListView.getHeight() * BOUND_GAP_RATIO);
        mTopScrollBound = (mDragListView.getTop() + boundGap);
        mBottomScrollBound = (mDragListView.getBottom() - boundGap);
    }

    protected boolean onInterceptTouchEvent(TouchEvent ev) {
        mFingerX = ev.getPointerPosition(0).getX();
        mFingerY = ev.getPointerPosition(0).getY();
        if (ev.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            mTouchDownForDragStartY = (int) ev.getPointerPosition(0).getY();
        }
        invokedDraggingStarted();
        return isDragging;
    }

    protected boolean onTouchEvent(TouchEvent event) {
        if (isLongPressClick) {
            mDragListView.handleSetListener();
            return false;
        }
        mFingerX = event.getPointerPosition(0).getX();
        mFingerY = event.getPointerPosition(0).getY();
        if (!isDragging) {
            return false;
        }
        int eX = (int) event.getPointerPosition(0).getX();
        int eY = (int) event.getPointerPosition(0).getY();
        switch (event.getAction()) {
            case TouchEvent.OTHER_POINT_DOWN:
            case TouchEvent.PRIMARY_POINT_DOWN:
                invokedDraggingStartedTouch();
                break;
            case TouchEvent.POINT_MOVE:
                invokedDraggingStarted();
                mLastDragY = eY;
                mDragListView.handleDragMoving(eX, eY);
                if (!mIsDragScrollerRunning && (Math.abs(mLastDragY - mTouchDownForDragStartY) >= 4 * mTouchSlop)) {
                    mIsDragScrollerRunning = true;
                    ensureScrollHandler();
                    mScrollHandler.postTask(mDragScroller, SCROLL_HANDLER_DELAY_MILLIS);
                }
                break;
            case TouchEvent.OTHER_POINT_UP:
            case TouchEvent.PRIMARY_POINT_UP:
            case TouchEvent.CANCEL:
                ensureScrollHandler();
                mScrollHandler.removeTask(mDragScroller);
                mIsDragScrollerRunning = false;
                mDragListView.handleDragFinished(eX, eY);
                isDragging = false;
                isInvokedDraggingStarted = false;
                break;
        }
        return true;
    }

    private void ensureScrollHandler() {
        if (mScrollHandler == null) {
            mScrollHandler = new EventHandler(EventRunner.create());
        }
    }

    private final Runnable mDragScroller = new Runnable() {
        @Override
        public void run() {

            if (mLastDragY <= mTopScrollBound) {
                mDecorView.getContext().getUITaskDispatcher().asyncDispatch(new Runnable() {
                    @Override
                    public void run() {
                        mDragListView.scrollBy(-DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS);
                    }
                });
            } else if (mLastDragY >= mBottomScrollBound) {
                mDecorView.getContext().getUITaskDispatcher().asyncDispatch(new Runnable() {
                    @Override
                    public void run() {
                        mDragListView.scrollBy(DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS);
                    }
                });
            }
            mScrollHandler.postTask(this, SCROLL_HANDLER_DELAY_MILLIS);
        }
    };

    @Override
    public boolean onDragStarted(int x, int y, Component view) {
        //TODO 拖动效果需要优化
        if (view instanceof ItemMainLayout) {
            mItemMainLayout = (ItemMainLayout) view;
        }
        mItemMainLayout.setPosition(x - deviationRangeX, y - deviationRangeY);
        return true;
    }


    @Override
    public void onDragMoving(int x, int y, Component view, SlideAndDragListView.OnDragDropListener listener) {
        if (mItemMainLayout != null) {
            mItemMainLayout.setPosition(x - deviationRangeX, y - deviationRangeY);
        }
    }

    @Override
    public void onDragFinished(int x, int y, SlideAndDragListView.OnDragDropListener listener) {
    }

    @Override
    public void onSetListener(SlideAndDragListView.OnDragDropListener listener) {

    }

}
